﻿namespace NoIdeaIoc;
abstract class ResolveUnitBase : IResolveUnit
{
    readonly ServiceLifetime serviceLifetime;
    readonly string keyName;
    public ResolveUnitBase(ServiceLifetime serviceLifetime, string keyName)
    {
        this.serviceLifetime = serviceLifetime;
        this.keyName = keyName;
    }
    /// <summary>
    /// 服务的缓存键名
    /// </summary>
    public string KeyName => keyName;
    /// <summary>
    /// 生命周期类型
    /// </summary>
    public ServiceLifetime ServiceLifetime => serviceLifetime;
    /// <summary>
    /// 是否泛型定义类(不可直接实例化)
    /// </summary>
    public virtual bool IsGenericTypeDefinition => false;
    /// <summary>
    /// 判断是否缓存键一致
    /// </summary>
    /// <param name="keyName"></param>
    /// <returns></returns>
    public bool EqualKeyName(string keyName) => this.keyName == keyName;
    public object? ResolveInstance(ILifeTime lifeTime, string keyName)
    {
        if (this.keyName != keyName) return null;
        var result = OnResolveInstance((LifeTime)lifeTime, keyName);
        return result;
    }
    protected abstract object? OnResolveInstance(LifeTime lifeTime, string keyName);
    public IocResponse? ResolveInstance(ILifeTime lifeTime, Type[] genericArgumentTypes, string keyName)
    {
        var result = OnResolveInstance((LifeTime)lifeTime, genericArgumentTypes, keyName);
        return result;
    }
    protected virtual IocResponse? OnResolveInstance(LifeTime lifeTime, Type[] genericArgumentTypes, string keyName) => throw new NotImplementedException();
    /// <summary>
    /// 获取缓存键名
    /// </summary>
    /// <param name="type"></param>
    /// <returns></returns>
    internal static string GetCacheKey(Type type)
    {
        return EmitHelper.GetFullName(type.FullName);
    }
    /// <summary>
    /// 获取缓存键名
    /// </summary>
    /// <param name="type"></param>
    /// <param name="keyName"></param>
    /// <returns></returns>
    internal static string GetCacheKey(Type type, string keyName)
    {
        var tempStr = GetCacheKey(type);
        if (keyName == null) return tempStr;
        return $"{tempStr}|{keyName}";
    }
    /// <summary>
    /// 获取缓存键名
    /// </summary>
    /// <param name="serviceKeyName"></param>
    /// <param name="keyName"></param>
    /// <returns></returns>
    internal static string GetCacheKey(string serviceKeyName, string keyName)
    {
        if (keyName == null) return serviceKeyName;
        return $"{serviceKeyName}|{keyName}";
    }
}
class ResolveUnitByFunc : ResolveUnitBase
{
    readonly Func<IServiceProvider, object> func;
    public ResolveUnitByFunc(Func<IServiceProvider, object> func, string keyName, ServiceLifetime serviceLifetime) : base(serviceLifetime, keyName)
    {
        this.func = func;
    }
    protected override object? OnResolveInstance(LifeTime lifeTime, string keyName)
    {
        return func(lifeTime);
    }
}
class ResolveUnitByKeyedFunc : ResolveUnitBase
{
    readonly Func<IServiceProvider, object?, object> func;
    public ResolveUnitByKeyedFunc(Func<IServiceProvider, object?, object> func, string keyName, ServiceLifetime serviceLifetime) : base(serviceLifetime, keyName)
    {
        this.func = func;
    }
    protected override object? OnResolveInstance(LifeTime lifeTime, string keyName)
    {
        return func(lifeTime, keyName);
    }
}
class ResolveUnitByServiceType : ResolveUnitBase
{
    readonly Func<LifeTime, string, object> func;
    public ResolveUnitByServiceType(Func<LifeTime, string, object> func, string keyName, ServiceLifetime serviceLifetime) : base(serviceLifetime, keyName)
    {
        this.func = func;
    }
    protected override object? OnResolveInstance(LifeTime lifeTime, string keyName)
    {
        return func(lifeTime, keyName);
    }
}
record GenericTypeDefinitionInfo(string typeFullNameMd5, Func<LifeTime, string, object> func);
class ResolveUnitByGenericTypeDefinition : ResolveUnitBase
{
    readonly Type serviceType;
    readonly Type genericTypeDefinitionType;
    public ResolveUnitByGenericTypeDefinition(Type serviceType, Type genericTypeDefinitionType, string keyName, ServiceLifetime serviceLifetime) : base(serviceLifetime, keyName)
    {
        this.serviceType = serviceType;
        this.genericTypeDefinitionType = genericTypeDefinitionType;
    }
    protected override object? OnResolveInstance(LifeTime lifeTime, string keyName)
    {
        throw new NotImplementedException();
    }
    /// <summary>
    /// 是否泛型定义类(不可直接实例化)
    /// </summary>
    public override bool IsGenericTypeDefinition => true;
    readonly object lockObj = new object();
    GenericTypeDefinitionInfo[] cacheFuncs;
    protected override IocResponse? OnResolveInstance(LifeTime lifeTime, Type[] genericArgumentTypes, string keyName)
    {
        if (genericArgumentTypes == null || genericArgumentTypes.Length < 1) return null;
        var resultType = genericTypeDefinitionType.MakeGenericType(genericArgumentTypes);
        var resultCacheKey = ResolveUnitBase.GetCacheKey(resultType, keyName);
        var tempFuncs = cacheFuncs;
        var tempFun = tempFuncs?.FirstOrDefault(x => x.typeFullNameMd5 == resultCacheKey);
        if (tempFun != null)
        {
            var resultObj = tempFun.func(lifeTime, keyName);
            if (resultObj == null) return null;
            return new IocResponse(resultCacheKey, resultObj, base.ServiceLifetime);
        }
        Func<LifeTime, string, object> resolveFunc;
        lock (lockObj)
        {
            tempFuncs = cacheFuncs;
            tempFun = tempFuncs?.FirstOrDefault(x => x.typeFullNameMd5 == resultCacheKey);
            if (tempFun != null)
            {
                var resultObj = tempFun.func(lifeTime, keyName);
                if (resultObj == null) return null;
                return new IocResponse(resultCacheKey, resultObj, base.ServiceLifetime);
            }
            var instanceServiceType = serviceType.MakeGenericType(genericArgumentTypes);
            var instanceType = genericTypeDefinitionType.MakeGenericType(genericArgumentTypes);
            MethodInfo resolveIocResponseMethod = LifeTime.GetResolveIocResponseMethod();
            MethodInfo getInstanceMethod = IocResponse.GetInstanceMethod();
            resolveFunc = DefaultContainerBuilder.CreateServiceActiveFunc(instanceServiceType, instanceType, resolveIocResponseMethod, getInstanceMethod);
            if (resolveFunc == null)
            {
                var resultObj = lifeTime.Resolve(instanceType, keyName);
                if (resultObj == null) return null;
                return new IocResponse(resultCacheKey, resultObj, base.ServiceLifetime);
            }
            tempFun = new GenericTypeDefinitionInfo(resultCacheKey, resolveFunc);
            int newCount;
            GenericTypeDefinitionInfo[] newFuncs;
            if (tempFuncs == null)
            {
                newCount = 1;
                newFuncs = new GenericTypeDefinitionInfo[newCount];
            }
            else
            {
                newCount = tempFuncs.Length + 1;
                newFuncs = new GenericTypeDefinitionInfo[newCount];
                for (var i = 0; i < tempFuncs.Length; i++)
                {
                    newFuncs[i] = tempFuncs[i];
                }
            }
            newFuncs[newCount - 1] = tempFun;
            Interlocked.Exchange(ref cacheFuncs, newFuncs);
        }
        var resultInstancej = resolveFunc(lifeTime, keyName);
        if (resultInstancej == null) return null;
        return new IocResponse(resultCacheKey, resultInstancej, base.ServiceLifetime);
    }
}
record getEnumerableFuncRecord(string typeFullNameMd5, Func<LifeTime, string, object> func);
class ResolveUnitByEnumerableTypeDefinition : ResolveUnitBase
{
    public ResolveUnitByEnumerableTypeDefinition(ServiceLifetime serviceLifetime) : base(serviceLifetime, null)
    {
    }
    protected override object? OnResolveInstance(LifeTime lifeTime, string keyName)
    {
        throw new NotImplementedException();
    }
    /// <summary>
    /// 是否泛型定义类(不可直接实例化)
    /// </summary>
    public override bool IsGenericTypeDefinition => true;
    readonly Type enumableType = typeof(IEnumerable<>);
    readonly object lockObj = new object();
    getEnumerableFuncRecord[] cacheFuncs;
    protected override IocResponse? OnResolveInstance(LifeTime lifeTime, Type[] genericArgumentTypes, string keyName)
    {
        if (genericArgumentTypes == null || genericArgumentTypes.Length != 1) return null;
        var resultType = enumableType.MakeGenericType(genericArgumentTypes);
        var resultCacheKey = ResolveUnitBase.GetCacheKey(resultType, keyName);
        var tempFuncs = cacheFuncs;
        var tempFun = tempFuncs?.FirstOrDefault(x => x.typeFullNameMd5 == resultCacheKey);
        if (tempFun != null)
        {
            var resultObj = tempFun.func(lifeTime, resultCacheKey);
            if (resultObj == null) return null;
            return new IocResponse(resultCacheKey, resultObj, base.ServiceLifetime);
        }
        Func<LifeTime, string, object> resolveFunc;
        lock (lockObj)
        {
            tempFuncs = cacheFuncs;
            if (tempFuncs != null)
            {
                tempFun = tempFuncs.FirstOrDefault(x => x.typeFullNameMd5 == resultCacheKey);
                if (tempFun != null)
                {
                    var resultObj = tempFun.func(lifeTime, resultCacheKey);
                    if (resultObj == null) return null;
                    return new IocResponse(resultCacheKey, resultObj, base.ServiceLifetime);
                }
            }
            resolveFunc = DefaultContainerBuilder.CreateServiceActiveIEnumerableFunc(genericArgumentTypes[0]);
            tempFun = new getEnumerableFuncRecord(resultCacheKey, resolveFunc);
            int newCount;
            getEnumerableFuncRecord[] newFuncs;
            if (tempFuncs == null)
            {
                newCount = 1;
                newFuncs = new getEnumerableFuncRecord[newCount];
            }
            else
            {
                newCount = tempFuncs.Length + 1;
                newFuncs = new getEnumerableFuncRecord[newCount];
                for (var i = 0; i < tempFuncs.Length; i++)
                {
                    newFuncs[i] = tempFuncs[i];
                }
            }
            newFuncs[newCount - 1] = tempFun;
            Interlocked.Exchange(ref cacheFuncs, newFuncs);
        }
        var resultInstancej = resolveFunc(lifeTime, resultCacheKey);
        if (resultInstancej == null) return null;
        return new IocResponse(resultCacheKey, resultInstancej, base.ServiceLifetime);
    }
}